let attribute = new Object()
attribute.name = 'bob'
attribute.age = 12

const obj1 = {
    ['      ']: 1,
};

obj1["      "] // 特殊字符只能通过中括号访问

//attribute[name] = 'bob'

let myObj = new Object(),
    str = "myString",
    rand = Math.random(), // 0.12345
    obj = new Object();

myObj.type = "Dot syntax";
myObj["date created"] = "String with space";

// 重点记忆
// 1. 如果中括号里面用变量，那么变量的值，就是属性的名
// 2. 变量的值在变成属性名之前，会进行一步 toString()，转成字符串
// 3. 变量名作为属性的优点：1. 统一方便管理 2. 通用性强，不易出错
myObj[str] = "String value"; // myObj["myString"] = "String value";
myObj[rand] = "Random Number";
myObj[obj] = "Object";
myObj[""] = "Even an empty string";
console.log(myObj['myString']); // 错误写法 myObj.str
console.log(myObj['0.12345']); // 错误写法 myObj.rand
console.log(myObj['[object Object]']); // 错误写法 myObj.obj
myObj[str];
myObj[rand];
myObj[obj];

/**
 * 重点
 * # 取出对象中所有【可访问】的属性名数组
 * 会背，会用
 */
Object.keys(myObj);

/**
 * 构造器函数
 * @param make
 * @param model
 * @param year
 * @constructor
 */
function Car1(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
}

/**
 * 重点
 * 什么时候要抽象成一个类？
 * # 当对象需要在很多地方重复出现的时候，对象就需要转化成类来创建
 */
class CarOwner {
    name = '';
    sex = '';
}

function displayCar() {
    var result = `A Beautiful ${this.year} ${this.make} ${this.model}`;
    console.log(result);
}

displayCar();

/**
 * 重点
 * 1. this 指向上一级作用域
 * 2. 创建的时候，作用域就定死了
 */
const displayCar2 = () => {
    console.log(this);
}

class Car2 {
    // # js可以省略定义，ts不能省略定义
    // make = '';
    // model = '';
    // year = '';
    /**
     * # 构造器函数 = 构造函数
     */
    constructor(make, model, year) {
        this.make = make;
        this.model = model;
        this.year = year;
        this.age = 12;
        this.displayCar = displayCar; // 将外部函数作为类的内部方法
        // 重点 this.displayCar 作用域被绑定成了类
        this.displayCar2 = displayCar2;
        // 重点 this.displayCar2 作用域不变
        // owner 所有者，车主
        this.owner1 = new CarOwner();
        this.owner2 = new CarOwner();
        this.owner3 = new CarOwner();
        this.owner4 = new CarOwner();
        this.owner5 = new CarOwner();
        this.engine1 = {
            cylinders: 4,
        };
        this.engine2 = {
            cylinders: 4,
        };
        this.engine3 = {
            cylinders: 4,
        };
        this.engine4 = {
            cylinders: 4,
        };
        this.engine5 = {
            cylinders: 4,
        };
        this['my field'] = '123';
    }
}

/**
 * # 对象可以嵌套
 */
var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: {value: 2.2}, owner: new CarOwner()}};
var myArray = [1, 2, 3, 4]

// # 用完就扔的场景，或者是全局不变的场景
const obj = {
    ['my field']: 123,
};
obj['my field'] = 456; // # 永久性的修改，复用性太差，不产生不可逆的冲突

// 不同的对象，不会产生干扰，不会有冲突
const car1 = new Car1('', '', '');
const car2 = new Car2('', '', '');

// 可以通过对象的属性名来修改属性值
car2.engine1 = '123';

function Add(obj) {
    var result = "";
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            result += i
        }
    }
    return result;
}

let app = Add('bob');
console.log(app)


function listAllProperties(o) {
    var objectToInspect;
    var result = [];

    for (
        objectToInspect = o;
        objectToInspect !== null;
        objectToInspect = Object.getPrototypeOf(objectToInspect)//每次迭代获取objectToInspect原型
    ) {
        result = result.concat(Object.getOwnPropertyNames(objectToInspect));//链接objectToInspect获取的属性
    }

    return result;
}


let obj1 = {
    property_1: 'value_1', // property_# 可以是一个标识符...
    2: 'value_2', // 或一个数字...
    ["property" + 3]: 'value_3', //  或一个可计算的 key 名
    "property n": 'value_n',// 或一个字符串
};

class Animal {

    constructor(name) {
        this.name = name;
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);
    }
}

let any = [, , , , , , , , ,]
any[5]
Car.prototype.color = null;
car1.color = "black";

/**
 * 重点
 * # 原型链的继承，与类的继承类似，也存在覆盖关系
 */
const obj10 = {
    name: '123',
    say() {
        console.log('我是子对象')
    },
    __proto__: {
        name: '456',
        age: 12,
        __proto__: {
            // 对象中函数的两种写法
            work: function () {
                console.log('父对象在工作')
            },
            say() {
                console.log('我是父对象')
            },
        }
    }
};

console.log(obj10.name); // 123
console.log(obj10.age); // 12
obj10.say();
obj10.work();

const obj11 = {
    name: 'bob',
};

obj11['name'] = '456';
obj11.name = '123';

const obj13 = {
    __name: '',
    get name() {
        // 重点 访问拦截，进行一些操作
        return `我的名字是 ${this.__name}`;
    },
    set name(val) {
        // 中的那 赋值拦截，进行一些操作
        this.__name = val.replace(/^\w/, ($1) => $1.toUpperCase());
    }
}

var d = Date.prototype;
/**
 * 重点 通过 Object.defineProperty 来定义一个属性
 */
Object.defineProperty(
    d, // 对象
    "year", // 属性名
    {
        enumerable: false, // 是否可枚举（是否可以在 for of 中循环出来）
        configurable: false, // 是否可修改（false是指year属性，无法再被 Object.defineProperty 定义）
        writable: false, // 是否可以写入（是否可赋值）
        get() {
            return this.getFullYear();
        },
        set(y) {
            this.setFullYear(y);
        },
    }
);
d.year;
d.year = 2025;

/**
 * 重点 通过 Object.defineProperties 来定义多个属性
 */
Object.defineProperties(d, {
    b: {
        get: function () {
            return this.a + 1;
        },
    },
    c: {
        set: function (x) {
            this.a = x / 2;
        },
    },
});

// 重点 删除后，该属性将不存在，访问会得到 undefined
delete d.year;

// 重点 引用类型：就是栈指向堆对象的指针